package ncrnadb.ncinetview.internal.request;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;


import ncrnadb.ncinetview.internal.cy.CyUtils;
import ncrnadb.ncinetview.internal.owrapper.EntityType;
import ncrnadb.ncinetview.internal.owrapper.ORDB;
import ncrnadb.ncinetview.internal.owrapper.RID;
import ncrnadb.ncinetview.internal.owrapper.RelationFields;

import org.cytoscape.model.CyColumn;
import org.cytoscape.model.CyEdge;
import org.cytoscape.model.CyNetwork;
import org.cytoscape.model.CyNode;
import org.cytoscape.model.CyRow;

import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientVertex;


public class ExpandRequest {

	public CyNetwork network = null;
	
	public CyColumn sourceTypeCol = null;
	public boolean sourceTypeNcrna = false;
	public boolean sourceTypeRna = false;
	public boolean sourceTypeGene = false;
	public boolean sourceTypeDisease = false;
	public boolean sourceTypeOthers = false;
	
	public CyColumn sourceNameCol = null;
	public boolean onlySelected = false;
	
	public boolean expand = false;
	public boolean expandWithNcrna = false;
	public boolean expandWithRna = false;
	public boolean expandWithGene = false;
	public boolean expandWithDisease = false;
	public boolean expandWithOthers = false;
	
	
	public ExpandRequest(){
	}
	
	
	
	public void solveRequest(){
		
		if(network!=null){
			//&& sourceTypeCol!=null && sourceNameCol!=null){
			System.out.println("OK");
			
			if(sourceTypeCol == null)
				sourceTypeCol = CyUtils.createTypeColumn(network);
			
			if(sourceNameCol == null)
				sourceNameCol = CyUtils.createAliasesColumn(network);
			
			System.out.println(sourceTypeCol +"&"+ sourceNameCol);
			
			/*===================================================================================================================*/
			//add columns to cynetwork, if they dot not exist
			CyUtils.addORIDColumns(network);
			
			
			/*===================================================================================================================*/
			//open db connection
			
			ORDB ordb = new ORDB();
			ordb.open();
			
			
			
			/*===================================================================================================================*/
			
			//map cynode -> bioentities
			Map<Long, Set<Vertex>> cy2ov = new TreeMap<Long, Set<Vertex>>();
			//map bioentity -> cynodes
			Map<Vertex, Set<Long>> ov2cy = new TreeMap<Vertex, Set<Long>>();
			
			
			/*===================================================================================================================*/
			//get selected nodes
			System.out.println("retrieving nodes...");
			
			Collection<CyRow> rows = null;
			if(onlySelected){
				rows = network.getDefaultNodeTable().getMatchingRows(CyNetwork.SELECTED, true);
			}
			else{
				rows = network.getDefaultNodeTable().getAllRows();
			}
			
			
			String tcName = sourceTypeCol.getName();
//			String acName = sourceNameCol.getName();
			String keycName = network.getDefaultNodeTable().getPrimaryKey().getName();
			
			
			for(CyRow row : rows){
				
				//get entity type, if one of the specified types
				// empty type field means unknown entity type
				String rowType = row.get(tcName, String.class);
				if(rowType == null)	rowType = "";
				EntityType eType = null;
				if(sourceTypeNcrna && rowType.compareToIgnoreCase("ncrna")==0)				eType = EntityType.NCRNA;
				else if(sourceTypeRna &&rowType.compareToIgnoreCase("rna")==0)				eType = EntityType.RNA;
				else if(sourceTypeGene && rowType.compareToIgnoreCase("gene")==0)			eType = EntityType.GENE;
				else if(sourceTypeDisease && rowType.compareToIgnoreCase("disease")==0)		eType = EntityType.DISEASE;
				else if(sourceTypeOthers)													eType = EntityType.OTHERS;
				
				
				if(eType != null){
					
					//get cyfields aliases RIDs
					Collection<String> rowAliases = CyUtils.getStringValues(row, sourceNameCol);
					Collection<RID> rids = RID.getRIDs(rowAliases, eType==EntityType.OTHERS ? null : eType);
					
					//get entity vertices from RIDs
					Collection<Vertex> ents = ordb.getEntities(rids);
					
					//set RID cycolumn value
					List<String> aa = new LinkedList<String>();
					for(Vertex v : ents){
						aa.add(v.getId().toString());
					}
					row.set(CyUtils.ORIDNodeColumnName, aa);
					
					
					Long rowId = row.get(keycName, Long.class);
					
					//map cy to OR and viceversa
					for(Vertex v : ents){
						Set<Long> nodes = ov2cy.get(v);
						if(nodes == null){
							nodes = new TreeSet<Long>();
							ov2cy.put(v, nodes);
						}
						
						Set<Vertex> verts = cy2ov.get(rowId);
						if(verts == null){
							verts = new TreeSet<Vertex>();
							cy2ov.put(rowId, verts);
						}
						
						nodes.add(rowId);
						verts.add(v);
					}
				}
			}
			
			
			
			/*===================================================================================================================*/
			//expand network with db neighbors
			System.out.println("retrieving neighbours...");
			
			if(expand){
				//types of neighbours to be included
				Set<Integer> neighTypes = new TreeSet<Integer>();
				if(expand){
					if(expandWithNcrna) neighTypes.add(ordb.getOEntityType(EntityType.NCRNA));
					if(expandWithRna) neighTypes.add(ordb.getOEntityType(EntityType.RNA));
					if(expandWithGene) neighTypes.add(ordb.getOEntityType(EntityType.GENE));
					if(expandWithDisease) neighTypes.add(ordb.getOEntityType(EntityType.DISEASE));
					if(expandWithOthers) neighTypes.add(ordb.getOEntityType(EntityType.OTHERS));
				}
				
				//we will add new cynodes and entities to maps, later
				Map<Vertex,Set<Long>> toAdd = new TreeMap<Vertex, Set<Long>>();
				
				for(Vertex v : ov2cy.keySet()){
					
					Collection<Vertex> neighs = ordb.getEntityNeighborsByClusters(v, neighTypes);
					
					for(Vertex n : neighs){
						if(!ov2cy.containsKey(n) && ! toAdd.containsKey(n)){
							//new entity to be add to cyto as new cynode
							CyNode cnode = CyUtils.addEntityToNetwork(ordb, n, network, sourceTypeCol, sourceNameCol);
						
							//add it to maps
							Set<Long> nodes = toAdd.get(n);
							if(nodes == null){
								nodes = new TreeSet<Long>();
								toAdd.put(n, nodes);
							}
							Set<Vertex> verts = cy2ov.get(cnode.getSUID());
							if(verts == null){
								verts = new TreeSet<Vertex>();
								cy2ov.put(cnode.getSUID(), verts);
							}
							nodes.add(cnode.getSUID());
							verts.add(v);
						}
					}
				}
			
				ov2cy.putAll(toAdd);
				toAdd = null;
			}
			
			
			/*===================================================================================================================*/
			//expand network with db edges
			System.out.println("retrieving edges...");

			Collection<Edge> edges =  ordb.getRelationsAmong(ov2cy.keySet());
			System.out.println("putting in cyto "+edges.size()+" edges...");
			for(Edge ed : edges){
				//System.out.println(ed);
				
				RelationFields rfs = ordb.getFields(ed);
				
				Collection<CyRow> matchingRows = network.getDefaultEdgeTable().getMatchingRows(CyUtils.ORIDEdgeColumnName, ed.getId().toString());
				if(matchingRows.isEmpty()){
					
					//System.out.println("-");
					
					Collection<Long> sources = ov2cy.get(ed.getVertex(Direction.OUT));
					Collection<Long> targets = ov2cy.get(ed.getVertex(Direction.IN));
					
					for(Long s : sources){
						for(Long t : targets){
							
							System.out.println(s + " - " + t);
							System.out.println(network.getNode(s) + " - " + network.getNode(t));
						
							CyEdge ced = network.addEdge(network.getNode(s), network.getNode(t), false);
							CyRow cedRow = network.getDefaultEdgeTable().getRow(ced.getSUID());
							cedRow.set("level", rfs.level);
							cedRow.set("pubmed", rfs.pubmedID);
							cedRow.set("support", rfs.supportSentence);
							cedRow.set("source", rfs.source.compact());
							cedRow.set(CyUtils.ORIDEdgeColumnName, ed.getId().toString());
						}
					}
				}
			}
			System.out.println("done.");
			
			ordb.close();
		}
	}
	
}
